package com.lumlord.common.spring;

import com.lumlord.common.annotation.rpc.Server;
import com.lumlord.common.spring.rpc.LumlordNetBridge;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.*;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

import org.springframework.util.Assert;

import java.io.IOException;
import java.util.*;

public class NetBridgeRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware , ApplicationContextAware {
    private ResourceLoader resourceLoader;
    private ApplicationContext applicationContext;
    private Environment environment;
    String basePackage = "com.lumlord.rpc";
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);

        Map<String, Object> attrs = metadata.getAnnotationAttributes(Server.class.getName());
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(Server.class);

        scanner.addIncludeFilter(annotationTypeFilter);
        register(scanner,registry,basePackage);

    }


    public void registerNetBridge(BeanDefinitionRegistry registry,String cls,String registerName){

     //   BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(cls);
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(cls);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.getPropertyValues().add("interfaceClass", definition.getBeanClassName());
        definition.getPropertyValues().add("environment", environment);
        definition.getPropertyValues().add("applicationContext",applicationContext);
        definition.setBeanClass(LumlordNetBridge.class);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        registry.registerBeanDefinition(registerName, definition);
    }

    public void register(ClassPathScanningCandidateComponentProvider scanner ,BeanDefinitionRegistry registry,String basePackage){
        Set<BeanDefinition> candidateComponents = scanner
                .findCandidateComponents(basePackage);
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                // verify annotated class is an interface
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
             //   Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");
                if(annotationMetadata.isInterface()){
                    String className = annotationMetadata.getClassName();
                    // 注册bean名,一般为类名首字母小写
                    String registerName = className.substring(className.lastIndexOf(".")+1,className.length());
                    registerName = (registerName.charAt(0)+"").toLowerCase()+registerName.substring(1);
                    registerNetBridge(registry,className,registerName);
                }

            }
        }
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(
                    AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }



    /**
     * Helper class to create a {@link TypeFilter} that matches if all the delegates
     * match.
     *
     * @author Oliver Gierke
     */
    private static class AllTypeFilter implements TypeFilter {

        private final List<TypeFilter> delegates;

        /**
         * Creates a new {@link AllTypeFilter} to match if all the given delegates match.
         *
         * @param delegates must not be {@literal null}.
         */
        AllTypeFilter(List<TypeFilter> delegates) {
            Assert.notNull(delegates, "This argument is required, it must not be null");
            this.delegates = delegates;
        }

        @Override
        public boolean match(MetadataReader metadataReader,
                             MetadataReaderFactory metadataReaderFactory) throws IOException {

            for (TypeFilter filter : this.delegates) {
                if (!filter.match(metadataReader, metadataReaderFactory)) {
                    return false;
                }
            }

            return true;
        }

    }

}
